﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace System.Windows.Controls
{
    /// <summary>
    /// Implements a simple toolbar class which supports push buttons and command (single-click) buttons.
    /// </summary>
    [TemplatePart(Name = "Root",     Type = typeof(Grid))]
    [TemplatePart(Name = "BaseFill", Type = typeof(Rectangle))]
    public class Toolbar : System.Windows.Controls.SelectionItemsControl
    {
        /// <summary>
        /// Fires when a <see cref="ToolbarButton"/> instance is clicked.
        /// </summary>
        public event EventHandler<ToolbarButtonEventArgs>   ButtonClicked = null;

        /// <summary>
        /// Initializes a <see cref="Toolbar"/> instance.
        /// </summary>
        public Toolbar()
        {
            this.DefaultStyleKey = typeof(Toolbar);
            this.Width = 200;
        }

        /// <summary>
        /// Identifies the <see cref="CornerRadius"/> dependency property
        /// </summary>
        public static DependencyProperty CornerRadiusProperty = RegisterDependencyProperty(typeof(Toolbar), "CornerRadius", typeof(CornerRadius));

        private ToolbarButtons  _Buttons = new ToolbarButtons();
        private Grid            root;
        private Rectangle       baseFill;

        /// <summary>
        /// (Override) Handles dependency property changes for this <see cref="Toolbar"/> instance.
        /// </summary>
        /// <param name="e"></param>
        protected override void OnDependencyPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            if (e.Property == CornerRadiusProperty)
            {
                this.CornerRadius = (CornerRadius)e.NewValue;
            }
            else
            {
                base.OnDependencyPropertyChanged(e);
            }
        }

        /// <summary>
        /// Gets/Sets a <see cref="System.Windows.CornerRadius"/> which defines the corner radii for the <see cref="Toolbar"/> instance.
        /// </summary>
        public CornerRadius CornerRadius
        {
            get { return (CornerRadius)GetValue(CornerRadiusProperty); }
            set { SetValue(CornerRadiusProperty, value); }
        }

        /// <summary>
        /// Gets a collection of <see cref="ToolbarButton"/> instances.
        /// </summary>
        public ToolbarButtons Buttons
        {
            get { return _Buttons; }
        }

        /// <summary>
        /// (Override) Processes the control template for this <see cref="Toolbar"/> instance.
        /// </summary>
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            //root = VerifyTemplateChild<Grid>("Root");
            //baseFill = VerifyTemplateChild<Rectangle>("BaseFill");
        }

        /// <summary>
        /// A new implementation to capture metrics during a standard Arrangement pass
        /// </summary>
        public new void Arrange(Rect rectFinal)
        {
            this.MinWidth = rectFinal.Width;
            base.Arrange(rectFinal);
        }

        internal void DispatchCommandClick(ToolbarButton button)
        {
            if (button != null)
            {
                if (!String.IsNullOrEmpty(button.GroupName))
                {
                    foreach (ToolbarButton child in this.Buttons)
                    {
                        if (child != button && String.Compare(child.GroupName, button.GroupName) == 0)
                        {
                            child.IsPressed = false;
                        }
                    }

                    button.IsPressed = true;
                }

                OnButtonClick(button);
            }
        }

        /// <summary>
        /// A virtual which fires the <see cref="ButtonClicked"/> event.
        /// </summary>
        /// <param name="button">The <see cref="ToolbarButton"/> button which was clicked</param>
        protected virtual void OnButtonClick(ToolbarButton button)
        {
            if (button != null && this.ButtonClicked != null) this.ButtonClicked(this, new ToolbarButtonEventArgs(button));
        }

        /// <summary>
        /// Examines an item to determine if it is the appropriate item container for this control
        /// </summary>
        /// <param name="item">The item to examine</param>
        /// <returns>A boolean value indicating that the item is the correct container (true) or not (false).</returns>
        protected override bool IsItemItsOwnContainerOverride(object item)
        {
            return (item is ToolbarButton);
        }

        /// <summary>
        /// Prepares an item element for insertion into this control
        /// </summary>
        /// <param name="element">The visual element to being inserted</param>
        /// <param name="item">The data item associated with the visual element</param>
        protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
        {
            base.PrepareContainerForItemOverride(element, item);
            if (element is ToolbarButton)
            {
                ToolbarButton button = (ToolbarButton)element;
                button.ParentToolbar = this;
                if (!_Buttons.Contains(button)) _Buttons.Add(button);
            }
        }

        /// <summary>
        /// Performs removal record-keeping for an item container used by this control
        /// </summary>
        /// <param name="element">The visual element being removed</param>
        /// <param name="item">The data item associated with the visual element</param>
        protected override void ClearContainerForItemOverride(DependencyObject element, object item)
        {
            base.ClearContainerForItemOverride(element, item);
            if (element is ToolbarButton)
            {
                ToolbarButton button = (ToolbarButton)element;
                if (_Buttons.Contains(button)) _Buttons.Remove(button);
            }
        }

        /// <summary>
        /// Provides a public method for setting the toggle state for a <see cref="ToolbarButton"/> which is in a push-button group
        /// </summary>
        /// <param name="button">The button to toggle</param>
        /// <param name="state">The new state of the button</param>
        public void SetButtonState(ToolbarButton button, bool state)
        {
            if (button != null && this.Buttons.Contains(button))
            {
                DispatchCommandClick(button);
            }
        }
    }

    /// <summary>
    /// Implements a derived <see cref="System.Windows.Controls.ButtonBase"/> class for use in a <see cref="Toolbar"/> control.
    /// </summary>
    public class ToolbarButton : System.Windows.Controls.ButtonBase
    {
        /// <summary>
        /// Instantiates a <see cref="ToolbarButton"/> instance.
        /// </summary>
        public ToolbarButton()
        {
            this.DefaultStyleKey = typeof(ToolbarButton);
            this.GroupName = String.Empty;
            this.IsPressed = false;
        }

        private const string    STYLE_COMMAND = "ToolbarButtonCommand";
        private const string    STYLE_SEPARATOR = "ToolbarButtonSeparator";
        private const string    STYLE_CONTROL = "ToolbarButtonControl";

        /// <summary>
        /// Identifies the <see cref="GroupName"/> dependency property.
        /// </summary>
        public static readonly DependencyProperty GroupNameProperty = RegisterDependencyProperty(typeof(ToolbarButton), "GroupName", typeof(string));

        /// <summary>
        /// Identifies the <see cref="IsPressed"/> dependency property.
        /// </summary>
        public static readonly DependencyProperty IsPressedProperty = RegisterDependencyProperty(typeof(ToolbarButton), "IsPressed", typeof(bool));

        /// <summary>
        /// Identifies the <see cref="Value"/> dependency property.
        /// </summary>
        public static readonly DependencyProperty ValueProperty = RegisterDependencyProperty(typeof(ToolbarButton), "Value", typeof(object));

        private Toolbar         _ParentToolbar;
        private ResourceControl symbol;
        private ResourcePath    _ResourcePath;
        private ContentPresenter contentPresenter;

        /// <summary>
        /// Loads a Xaml resource from an assembly
        /// </summary>
        /// <param name="assemblyType">The <see cref="System.Type"/> which identifies the assembly</param>
        /// <param name="resourceName">The identifier for the resource including namespace</param>
        /// <seealso cref="ResourceControl"/>
        public override void LoadResource(Type assemblyType, string resourceName)
        {
            _ResourcePath = new ResourcePath(assemblyType, resourceName);
        }


        /// <summary>
        /// (Override) Processes the control template for this <see cref="ToolbarButton"/> instance.
        /// </summary>
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            symbol = VerifyTemplateChild<ResourceControl>("Symbol");
            if (symbol != null && _ResourcePath != null)
            {
                symbol.LoadResource(_ResourcePath);
            }

            contentPresenter = VerifyTemplateChild<ContentPresenter>("contentPresenter");
            if (contentPresenter != null)
            {
                contentPresenter.Content = this.Content;
                contentPresenter.Visibility = (contentPresenter.Content == null ? Visibility.Collapsed : Visibility.Visible);
            }

            GoToState(true, (this.IsPressed ? "Pressed" : "Normal"));
        }

        internal Toolbar ParentToolbar
        {
            get { return _ParentToolbar; }
            set { _ParentToolbar = value; }
        }

        /// <summary>
        /// (Override) Handles dependency property changes for the <see cref="ToolbarButton"/> instance.
        /// </summary>
        /// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> event argument</param>
        protected override void OnDependencyPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            if (e.Property == GroupNameProperty)
            {
                this.GroupName = (e.NewValue == null ? String.Empty : e.NewValue.ToString());
            }
            else if (e.Property == IsPressedProperty)
            {
                this.IsPressed = (bool)e.NewValue;
            }
            else if (e.Property == ValueProperty)
            {
                this.Value = e.NewValue;
            }
            else
            {
                base.OnDependencyPropertyChanged(e);
            }
        }

        /// <summary>
        /// (Override) Method is called when the content has changed.
        /// </summary>
        /// <param name="oldContent">The oprinal content</param>
        /// <param name="newContent">The new content</param>
        protected override void OnContentChanged(object oldContent, object newContent)
        {
            base.OnContentChanged(oldContent, newContent);
            if (contentPresenter != null)
            {
                contentPresenter.Content = newContent;
                contentPresenter.Visibility = (contentPresenter.Content == null ? Visibility.Collapsed : Visibility.Visible);
            }
        }

        /// <summary>
        /// Gets/Sets the group name for a toggle <see cref="ToolbarButton"/> instance.
        /// <remarks>Setting this value to null or String.Empty defines the button to be a command or single click button.</remarks>
        /// </summary>
        public string GroupName
        {
            get { object val = GetValue(GroupNameProperty); return (val != null ? val.ToString() : String.Empty); }
            set { SetValue(GroupNameProperty, value); }
        }

        /// <summary>
        /// Gets/Sets a programmatically defines value to associate with the <see cref="ToolbarButton"/>
        /// </summary>
        public object Value
        {
            get { return GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }

        /// <summary>
        /// Gets/Sets a boolean value indicating whether the <see cref="ToolbarButton"/> is currently pressed.
        /// </summary>
        public bool IsPressed
        {
            get { return (bool)GetValue(IsPressedProperty); }
            set { SetValue(IsPressedProperty, value); GoToState(true, (value ? "Pressed" : "Normal")); }
        }

        /// <summary>
        /// (Override) Handles the Mouse Enter event for the <see cref="ToolbarButton"/>.
        /// </summary>
        /// <param name="e">The <see cref="System.Windows.Input.MouseEventArgs"/> for the event</param>
        protected override void OnMouseEnter(MouseEventArgs e)
        {
            if (!this.IsPressed)
            {
                base.OnMouseEnter(e);
            }
        }

        /// <summary>
        /// (Override) Handles the Mouse Leave event for the <see cref="ToolbarButton"/>.
        /// </summary>
        /// <param name="e">The <see cref="System.Windows.Input.MouseEventArgs"/> for the event</param>
        protected override void OnMouseLeave(MouseEventArgs e)
        {
            if (!this.IsPressed)
            {
                base.OnMouseLeave(e);
            }
        }

        /// <summary>
        /// (Override) Handles the Mouse Left Button Up event for the <see cref="ToolbarButton"/>.
        /// </summary>
        /// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/> for the event</param>
        protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
        {
            if (String.IsNullOrEmpty(this.GroupName))
            {
                base.OnMouseLeftButtonUp(e);
                this.ParentToolbar.DispatchCommandClick(this);
            }
            else
            {
                if (this.IsEnabled)
                {
                    this.ReleaseMouseCapture();

                    this.ParentToolbar.DispatchCommandClick(this);

                    Point pt = e.GetPosition(this);
                    if (!this.IsPressed)
                    {
                        if (this.ClientRect.Contains(pt))
                        {
                            GoToState(true, "MouseOver");
                        }
                        else
                        {
                            GoToState(true, "Normal");
                        }
                    }
                }
            }
        }
    }

    /// <summary>
    /// Implements a seperator element for a <see cref="Toolbar"/> control.
    /// </summary>
    public class ToolbarSeperator : System.Windows.Controls.ControlBase
    {
        /// <summary>
        /// Instantiates a <see cref="ToolbarSeperator"/> instance.
        /// </summary>
        public ToolbarSeperator()
        {
            this.DefaultStyleKey = typeof(ToolbarSeperator);
        }
    }

    /// <summary>
    /// Implements a collection of <see cref="ToolbarButton"/> instances.
    /// </summary>
    public class ToolbarButtons : List<ToolbarButton>
    {

    }

    /// <summary>
    /// Implements an event argument for expressing context of a single <see cref="ToolbarButton"/>
    /// </summary>
    public class ToolbarButtonEventArgs : EventArgs
    {
        /// <summary>
        /// Identifies the <see cref="ToolbarButton"/> instance.
        /// </summary>
        public readonly ToolbarButton   Button = null;

        /// <summary>
        /// Instantiates a <see cref="ToolbarButtonEventArgs"/> event argument
        /// </summary>
        /// <param name="button">The <see cref="ToolbarButton"/> instance</param>
        public ToolbarButtonEventArgs(ToolbarButton button)
        {
            this.Button = button;
        }
    }
}
